﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Data;

namespace Flagwind.Data
{
	/// <summary>
	/// 描述实体类的全局字段信息，以一种更方便的方式访问实体类属性和对应的字段。
	/// </summary>
	public class EntityInfo
	{
		#region 公共属性

		/// <summary>
		/// 获取当前实体。
		/// </summary>
		public IEntity Entity
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取实体类对应的字段名称数组。
		/// </summary>
		public string[] FieldNames
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取实体类属性名称数组。
		/// </summary>
		public string[] PropertyNames
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取实体类属性类型数组。
		/// </summary>
		public Type[] PropertyTypes
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取实体类对应的表名称。
		/// </summary>
		public string TableName
		{
			get;
			private set;
		}

		#endregion

		#region 构造方法

		/// <summary>
		/// 初始化 EntityInfo 类的新实例。
		/// </summary>
		/// <param name="entityType">实体类型。</param>
		public EntityInfo(Type entityType)
		{
			var entity = Activator.CreateInstance(entityType) as IEntity;
			if(entity == null)
			{
				throw new ArgumentException(string.Format("{0} 不是继承自 Flagwind.Data.IEntity 的实体类型。", entityType.FullName));
			}
			this.Entity = entity;
			// 获取表名
			this.TableName = entity.GetMappingName();
			//设置虚拟属性写入标记
			entity.TestWriteProperty();

			// 获取所有公共属性
			var properties = entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
			//			var properties = entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

			var fieldNames = new List<string>();
			var propertyNames = new List<string>();
			var propertyTypes = new List<Type>();
			string lastField = string.Empty;
			foreach(PropertyInfo property in properties)
			{
				//获得调用的字段名称
				var type = property.PropertyType;
				// 跳过只读属性
				if(!property.CanWrite)
				{
					continue;
				}
				try
				{
					//这里需要设置属性，以便获取字段长度
					object value = null;
					if(type != typeof(string) && type != typeof(byte[]))
					{
						value = Activator.CreateInstance(type);
					}
					//这里可能有普通属性在被赋值
					property.SetValue(entity, value, null);
					var field = entity.GetChangedFieldName();
					//跟之前的对比，确定当前是属性字段对应的属性
					if(lastField != field)
					{
						fieldNames.Add(field);
						propertyNames.Add(property.Name);
						propertyTypes.Add(type);
						lastField = field;
					}
				}
				catch
				{
				}
			}
			this.FieldNames = fieldNames.ToArray();
			this.PropertyNames = propertyNames.ToArray();
			this.PropertyTypes = propertyTypes.ToArray();
		}

		#endregion

		#region 公共方法

		/// <summary>
		/// 根据指定的字段名获取对应的属性名。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <returns>属性名称。</returns>
		public string GetPropertyName(string fieldName)
		{
			if(this.FieldNames == null || this.PropertyNames == null)
			{
				return null;
			}
			for(int i = 0; i < this.FieldNames.Length; i++)
			{
				if(this.FieldNames[i].Equals(fieldName))
				{
					return this.PropertyNames[i];
				}
			}
			return null;
		}

		/// <summary>
		/// 根据指定的字段名获取对应的属性类型。
		/// </summary>
		/// <param name="fieldName">字段名称。</param>
		/// <returns>属性类型。</returns>
		public Type GetPropertyType(string fieldName)
		{
			if(this.FieldNames == null || PropertyTypes == null)
			{
				return null;
			}
			for(int i = 0; i < this.FieldNames.Length; i++)
			{
				if(this.FieldNames[i].Equals(fieldName))
				{
					return PropertyTypes[i];
				}
			}
			return null;
		}

		/// <summary>
		/// 根据指定的属性名获取对应的字段名。
		/// </summary>
		/// <param name="propertyName">属性名称。</param>
		/// <returns>字段名称。</returns>
		public string GetFieldName(string propertyName)
		{
			if(this.PropertyNames == null || this.FieldNames == null)
			{
				return null;
			}
			for(int i = 0; i < this.PropertyNames.Length; i++)
			{
				if(this.PropertyNames[i].Equals(propertyName))
				{
					return this.FieldNames[i];
				}
			}
			return null;
		}

		/// <summary>
		/// 为实体类的一个属性创建对应的数据库表的列的脚本。
		/// </summary>
		/// <param name="provider">数据访问提供者。</param>
		/// <param name="entity">实体实例。</param>
		/// <param name="field">字段名称。</param>
		/// <returns>创建数据库列的脚本。</returns>
		public string CreateColumnScript(IDataProvider provider, IEntity entity, string field)
		{
			var propertyType = this.GetPropertyType(field);
			var defaultValue = propertyType == typeof(string) ? string.Empty : Activator.CreateInstance(propertyType);
			;
			var parameter = provider.GetParameter(field, defaultValue);
			var buffer = string.Empty;
			if(propertyType == typeof(string))
			{
				int length = entity.GetFieldSize(field);
				//实体类未定义属性字段的长度
				if(length == -1)
				{
					string fieldType = "text";
					//此处要求SqlServer 2005以上，SqlServer2000 不支持
					if(provider.ProviderType == DataProviderType.SqlServer)
					{
						fieldType = "varchar(max)";
					}
					buffer = buffer + "[" + field + "] " + fieldType;
				}
				else
				{
					buffer = buffer + "[" + field + "] varchar" + "(" + length + ")";
				}
			}
			else
			{
				buffer = buffer + "[" + field + "] " + provider.GetNativeDbTypeName(parameter);
			}
			if(field == entity.GetIdentity())
			{
				if(provider.ProviderType == DataProviderType.SqlServer || provider.ProviderType == DataProviderType.SqlServerCe)
				{
					buffer = buffer + " IDENTITY(1,1)";
				}
				else if(provider.ProviderType == DataProviderType.Access)
				{
					buffer = buffer.Replace("Integer", " autoincrement");
				}
				else if(provider.ProviderType == DataProviderType.SQLite)
				{
					buffer = buffer + " autoincrement";
				}
				else if(provider.ProviderType == DataProviderType.MySql)
				{
					buffer = buffer + " AUTO_INCREMENT";
				}
				else if(provider.ProviderType == DataProviderType.PostgreSQL)
				{
					buffer = buffer + " DEFAULT nextval('" + entity.GetMappingName() + "_" + entity.GetIdentity() + "_" + "seq'::regclass) NOT NULL";
				}
				else
				{
					//Oracle 采用序列和触发器,这里不处理 
				}
			}
			//identity(1,1) primary key
			//Access 要求主键申明必须在自增之后，否则语法错误
			if(entity.GetPrimaryKey().Contains(field))
			{
				buffer = buffer + " PRIMARY KEY";
			}
			return provider.PrepareSQL(buffer);
		}

		#endregion
	}
}